﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel.Design;
using System.Collections;
using System.Windows.Forms.Design;

namespace System
{
    /// <summary>
    /// Design time Extension methods
    /// </summary>
    public static class DesignExt
    {
        /// <summary>
        /// Gets the service.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="provider">The provider.</param>
        /// <returns></returns>
        public static T GetService<T>(this IServiceProvider provider)
        {
            return (T)provider.GetService(typeof(T));
        }
        /// <summary>
        /// Gets the types.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="baseType">Type of the base.</param>
        /// <param name="excludeGlobal">if set to <c>true</c> [exclude global].</param>
        /// <param name="includeSelf">if set to <c>true</c> [include self].</param>
        /// <returns></returns>
        public static Type[] GetTypes(this IServiceProvider provider, Type baseType, bool excludeGlobal = true, bool includeSelf = false)
        {
            List<Type> list = new List<Type>();
            if (provider != null)
            {
                ITypeDiscoveryService iTypeDiscoveryService = provider.GetService<ITypeDiscoveryService>();
                if (iTypeDiscoveryService != null)
                {
                    try
                    {
                        ICollection coll = iTypeDiscoveryService.GetTypes(baseType, excludeGlobal);
                        if (coll != null)
                        {
                            foreach (Type t in coll)
                            {
                                if (includeSelf || !t.IsEquivalentTo(baseType))
                                    list.Add(t);
                            }
                        }
                    }
                    catch { }
                }
                if (list.Count == 0)
                {
                    try
                    {
                        IDesignerHost host = provider.GetService<IDesignerHost>();
                        if (host != null)
                        {
                            Type t = Type.GetType(host.RootComponentClassName);
                            if (t != null)
                            {
                                list.AddRange(t.Assembly.GetTypes(baseType, includeSelf, true, excludeGlobal));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        ex.LogInternal();
                    }
                }

                if (list.Count == 0)
                {
                    //PropertyGrid + DevExpress PropertyGridControl
                    object obj = provider.GetPropertyValue("SelectedObject");
                    if (obj != null && obj is System.ComponentModel.IComponent)
                    {
                        System.ComponentModel.IComponent comp = obj as System.ComponentModel.IComponent;
                        if (comp.Site != null)
                        {
                            list.AddRange(comp.Site.GetTypes(baseType, excludeGlobal));
                        }
                    }
                }
            }
            return list.ToArray();
        }
        /// <summary>
        /// Gets the type.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="typeFullName">Full name of the type.</param>
        /// <param name="excludeGlobalTypes">if set to <c>true</c> [exclude global types].</param>
        /// <returns></returns>
        public static Type GetType(this IServiceProvider provider, string typeFullName, bool excludeGlobalTypes = true)
        {
            if (provider != null)
            {
                Type t1 = Type.GetType(typeFullName);
                if (t1 != null)
                    return t1;

                ITypeDiscoveryService iTypeDiscoveryService = provider.GetService<ITypeDiscoveryService>();
                if (iTypeDiscoveryService != null)
                {
                    ICollection coll = iTypeDiscoveryService.GetTypes(typeof(object), excludeGlobalTypes);
                    foreach (Type t in coll)
                    {
                        if (t.FullName == typeFullName)
                        {
                            return t;
                        }
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// Filters the specified t.
        /// </summary>
        /// <param name="t">The t.</param>
        /// <param name="obj">The object.</param>
        /// <returns></returns>
        private static bool Filter(Type t, object obj)
        {
            Type baseType = obj as Type;
            if (t.IsEquivalentTo(baseType))
            {
                return true;
            }
            else if (t.IsAssignableFrom(baseType))
            {
                return true;
            }
            else if (t.IsSubclassOf(baseType))
            {
                return true;
            }
            else
            {
                Type tmp = t;
                while (tmp.BaseType != null && tmp.BaseType != typeof(object))
                {
                    if (tmp.BaseType.FullName == baseType.FullName)
                    {
                        return true;
                    }
                    tmp = tmp.BaseType;
                }
            }
            return false;
        }
        /// <summary>
        /// Gets the types.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <param name="baseType">Type of the base.</param>
        /// <param name="includeSelf">if set to <c>true</c> [include self].</param>
        /// <param name="includeReferencedAssemblies">if set to <c>true</c> [include referenced assemblies].</param>
        /// <param name="skipGAC">if set to <c>true</c> [skip gac].</param>
        /// <returns></returns>
        public static Type[] GetTypes(this Assembly assembly, Type baseType, bool includeSelf = true, bool includeReferencedAssemblies = true, bool skipGAC = true)
        {
            List<Type> list = new List<Type>();
            Type[] types = null;
            try
            {
                types = assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException rtlex)
            {
                types = rtlex.Types;
            }
            catch (Exception ex)
            {
                ex.LogInternal();
            }
            string baseName = baseType.FullName;
            Type tmp;

            if (types != null)
            {
                foreach (Type t in types)
                {
                    if (t == null)
                        continue;
                    if (t.FullName == baseName && includeSelf)
                    {
                        list.Add(t);
                    }
                    else if (t.IsAssignableFrom(baseType))
                    {
                        list.Add(t);
                    }
                    else if (t.IsSubclassOf(baseType))
                    {
                        list.Add(t);
                    }
                    else
                    {
                        tmp = t;
                        while (tmp.BaseType != null && tmp.BaseType != typeof(object))
                        {
                            if (tmp.BaseType.FullName == baseName)
                            {
                                list.Add(t);
                                break;
                            }
                            tmp = tmp.BaseType;
                        }
                    }
                }
            }
            if (includeReferencedAssemblies)
            {
                AssemblyName[] ass = assembly.GetReferencedAssemblies();
                if (ass != null)
                {
                    Assembly a;
                    foreach (AssemblyName n in ass)
                    {
                        try
                        {
                            a = Assembly.Load(n);
                            if (a.GlobalAssemblyCache && skipGAC)
                                continue;
                            if (a.FullName != assembly.FullName)
                            {
                                types = a.GetTypes(baseType, false, true, skipGAC);
                                list.AddRange(types);
                            }
                        }
                        catch { }
                    }
                }
            }
            return list.ToArray();
        }
    }
}
